package org.unidata.mdm.data.service.segments;

import java.util.List;
import java.util.Objects;

import org.unidata.mdm.core.context.DataRecordContext;
import org.unidata.mdm.core.type.data.ArrayAttribute;
import org.unidata.mdm.core.type.data.Attribute;
import org.unidata.mdm.core.type.data.CodeAttribute;
import org.unidata.mdm.core.type.data.ComplexAttribute;
import org.unidata.mdm.core.type.data.DataRecord;
import org.unidata.mdm.core.type.data.SimpleAttribute;
import org.unidata.mdm.core.type.data.impl.AbstractArrayAttribute;
import org.unidata.mdm.core.type.data.impl.AbstractCodeAttribute;
import org.unidata.mdm.core.type.data.impl.AbstractSimpleAttribute;
import org.unidata.mdm.core.type.data.impl.ComplexAttributeImpl;
import org.unidata.mdm.core.type.model.AttributeElement;
import org.unidata.mdm.core.type.model.EntityElement;

/**
 * Attribute autogeneration routines.
 * @author Mikhail Mikhailov on May 18, 2020
 */
public interface AttributesAutogenerationSupport {
    /**
     * Process fields.
     * @param wrapper
     * @param ctx
     */
    default void setupAttributesAutogeneration(EntityElement wrapper, DataRecordContext ctx) {

        if (Objects.isNull(wrapper) || !wrapper.hasGeneratingAttributes() || Objects.isNull(ctx.getRecord())) {
            return;
        }

        DataRecord record = ctx.getRecord();
        for(AttributeElement v : wrapper.getGeneratingAttributes()) {

            Attribute attribute = record.getAttribute(v.getName());
            if (attribute == null || !attribute.isEmpty()) {

                Object value = v.getGenerating().generate(ctx);
                if (value == null) {
                    continue;
                }

                if (attribute == null) {
                    createAttribute(v, record, value);
                } else {
                    updateAttribute(v, attribute, value);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    private void createAttribute(AttributeElement v, DataRecord record, Object value) {
        if (v.isCode()) {
            record.addAttribute(AbstractCodeAttribute.of(CodeAttribute.CodeDataType.fromModelType(v.getValueType()), v.getName(), value));
        } else if (v.isSimple()) {
            if (v.isDictionary()) {
                record.addAttribute(AbstractSimpleAttribute.of(SimpleAttribute.SimpleDataType.DICTIONARY, v.getName(), value));
            } else if (v.isEnumValue()) {
                record.addAttribute(AbstractSimpleAttribute.of(SimpleAttribute.SimpleDataType.ENUM, v.getName(), value));
            } else if (v.isLinkTemplate()) {
                record.addAttribute(AbstractSimpleAttribute.of(SimpleAttribute.SimpleDataType.LINK, v.getName(), value));
            } else {
                record.addAttribute(AbstractSimpleAttribute.of(SimpleAttribute.SimpleDataType.fromModelType(v.getValueType()), v.getName(), value));
            }
        } else if (v.isArray()) {
            if (v.isDictionary()) {
                record.addAttribute(AbstractArrayAttribute.of(ArrayAttribute.ArrayDataType.DICTIONARY, v.getName(), (List<?>) value));
            } else {
                record.addAttribute(AbstractArrayAttribute.of(ArrayAttribute.ArrayDataType.fromModelType(v.getValueType()), v.getName(), (List<?>) value));
            }
        } else if (v.isComplex()) {
            record.addAttribute(new ComplexAttributeImpl(v.getName(), (List<DataRecord>) value));
        }
    }

    @SuppressWarnings("unchecked")
    private void updateAttribute(AttributeElement v, Attribute attribute, Object value) {
        if (v.isCode()) {
            ((CodeAttribute<?>) attribute).castValue(value);
        } else if (v.isSimple()) {
            ((SimpleAttribute<?>) attribute).castValue(value);
        } else if (v.isArray()) {
            ((ArrayAttribute<?>) attribute).castValue((List<?>) value);
        } else if (v.isComplex()) {
            ((ComplexAttribute) attribute).addAll((List<DataRecord>) value);
        }
    }
}
